Skip to content

Conversation

@pyramation
Copy link
Contributor

@pyramation pyramation commented Jan 11, 2026

Summary

Adds a new Knative cloud function that transforms schema names in SQL using AST-based parsing with plpgsql-parser. The function accepts SQL and a schema mapping, then returns the transformed SQL with schema names replaced according to the mapping.

The transformation logic is adapted from @constructive-db/introspection's transform-schemas-ast.ts and handles:

  • Schema-qualified identifiers in RangeVar nodes (schema.table in SELECT/FROM)
  • Schema names in CreateSchemaStmt, DropStmt, GrantStmt nodes
  • DDL statements with embedded relations: CreateStmt (CREATE TABLE), IndexStmt, AlterTableStmt, TruncateStmt, CreateTrigStmt, RuleStmt, CreatePolicyStmt
  • Schema-qualified function names in FuncCall nodes
  • Schema-qualified type names in TypeName nodes
  • Schema names in VariableSetStmt (SET search_path)
  • Schema names inside PL/pgSQL function bodies (hydrated AST)

API:

POST /
{
  "sql": "SELECT * FROM old_schema.users",
  "schema_mapping": { "old_schema": "new_schema" }
}
// Returns: { transformed_sql, schemas_found, schemas_transformed }

Updates since last revision

Fixed a bug where DDL statements like CREATE TABLE old_schema.users were not being transformed. The @pgsql/traverse walker looks for nodes by their key name (e.g., RangeVar), but DDL statements like CreateStmt store their relation directly without the RangeVar wrapper. Added explicit handlers for CreateStmt, IndexStmt, AlterTableStmt, TruncateStmt, CreateTrigStmt, RuleStmt, and CreatePolicyStmt.

Local testing confirmed the following transformations now work:

  • CREATE SCHEMA old_schemaCREATE SCHEMA new_schema
  • SET search_path TO old_schemaSET search_path TO new_schema
  • CREATE TABLE old_schema.usersCREATE TABLE new_schema.users
  • GRANT USAGE ON SCHEMA old_schemaGRANT USAGE ON SCHEMA new_schema
  • SELECT * FROM old_schema.usersSELECT * FROM new_schema.users
  • PL/pgSQL function bodies with schema-qualified calls

Review & Testing Checklist for Human

  • Verify DDL coverage is complete - Other DDL statements may need similar handlers (e.g., ViewStmt, CreateSeqStmt, CommentStmt). Compare against the original transform-schemas-ast.ts in constructive-db
  • Test edge cases - Quoted identifiers, nested schema references, %TYPE/%ROWTYPE suffixes
  • Deploy and invoke - Verify the function can be deployed to Knative and invoked via HTTP

Recommended test plan:

  1. Build and run locally: cd functions/sql-schema-transform && pnpm build && PORT=8080 node dist/index.js
  2. Test with curl:
curl -X POST http://localhost:8080 \
  -H "Content-Type: application/json" \
  -d '{"sql": "CREATE SCHEMA old_schema; SET search_path TO old_schema; CREATE TABLE old_schema.users (id uuid, name text); GRANT USAGE ON SCHEMA old_schema TO authenticated; SELECT * FROM old_schema.users; CREATE FUNCTION old_schema.test() RETURNS void AS $$ BEGIN PERFORM old_schema.other_fn(); END; $$ LANGUAGE plpgsql;", "schema_mapping": {"old_schema": "new_schema"}}'

Notes

  • No unit tests were added - consider adding tests before production use
  • No README was added (unlike other functions in this directory)
  • Uses any types for AST nodes which is consistent with the source code but reduces type safety
  • Local testing was performed and a bug was found and fixed during development

Requested by: @pyramation
Link to Devin run: https://app.devin.ai/sessions/2eed6a2d4f0142349bb3bdd50334b23f

Adds a TypeScript cloud function that transforms schema names in SQL
using AST-based parsing with plpgsql-parser. This uses the hydrated
heterogeneous parser and de-parser to properly handle both SQL and
PL/pgSQL function bodies.

The function accepts SQL and a schema mapping, and returns the
transformed SQL with schema names replaced according to the mapping.

Based on the transformer code from @constructive-db/introspection.
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

The @pgsql/traverse walker looks for nodes by their key name (like RangeVar),
but in CreateStmt and other DDL statements, the relation is stored directly
without the RangeVar wrapper. Added handlers for:
- CreateStmt (CREATE TABLE)
- IndexStmt (CREATE INDEX)
- AlterTableStmt (ALTER TABLE)
- TruncateStmt (TRUNCATE)
- CreateTrigStmt (CREATE TRIGGER)
- RuleStmt (CREATE RULE)
- CreatePolicyStmt (CREATE POLICY)

Also fixed the schemas_transformed map to use the original schema name as the
key before mutation.
@devin-ai-integration
Copy link
Contributor

Closing - PR was created in the wrong repo. Moving to constructive-functions.

@devin-ai-integration devin-ai-integration bot deleted the devin/1768143263-sql-schema-transform-fn branch January 11, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants